﻿
using System;
using System.Collections.Generic;
using System.Linq;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;


namespace lights
{
    public class comp_polymesh : comp_poly
    {
        public float height;
        public bool useStep;
        public float heightStep;
        public bool mirror;

        public void InitQuad()
        {
            points = new List<Vector2>();
            points.Add(new Vector2(-1, -1));
            points.Add(new Vector2(1, -1));
            points.Add(new Vector2(1, 1));
            points.Add(new Vector2(-1, 1));

            this.height = 0.5f;
            this.useStep = true;
            this.heightStep = 0.1f;
            this.mirror = false;
            this.Bake();
        }
        static TriangleNet.Meshing.ConstraintOptions trier_config;
        static TriangleNet.Meshing.ITriangulator trier = new TriangleNet.Meshing.Algorithm.Dwyer();

        void GenPoly(IList<Vector3> vout, IList<int> iout, IList<Vector3> srcvertex, IList<int> srctris, float height, bool withFace, bool flip)
        {
            int indexstart = vout.Count;
            foreach (var v in srcvertex)
            {
                vout.Add(new Vector3((float)v.x, (float)v.y, height));
                //bottombegin++;
            }
            if (withFace)
            {
                for (var i = 0; i < srctris.Count / 3; i++)
                {
                    var i0 = srctris[i * 3];
                    var i1 = srctris[i * 3 + 1];
                    var i2 = srctris[i * 3 + 2];
                    iout.Add(indexstart + i0);
                    iout.Add(indexstart + (flip ? i2 : i1));
                    iout.Add(indexstart + (flip ? i1 : i2));
                }
            }
        }
        void GenPolyStep(IList<Vector3> vout, IList<int> iout, IList<Vector3> srcvertex, IList<int> srctris, float height, float step, bool flip)
        {
            int indexstart = vout.Count;
            for (var i = 0; i < srcvertex.Count; i++)
            {
                var now = srcvertex[i];
                var last = i - 1;
                if (last < 0) last = srcvertex.Count - 1;
                var next = i + 1;
                if (next >= srcvertex.Count)
                    next = 0;

                var v0 = (srcvertex[last] - now).normalized;
                var v1 = (srcvertex[next] - now).normalized;
                var crossz = Vector3.Cross(v0, v1).z;
                var half = (v0 + v1).normalized;
                if (crossz > 0)
                    half *= -1;
                vout.Add(new Vector3(now.x + half.x * this.heightStep, now.y + half.y * this.heightStep, height));

            }
            //if (withFace)
            {
                for (var i = 0; i < srctris.Count / 3; i++)
                {
                    var i0 = srctris[i * 3];
                    var i1 = srctris[i * 3 + 1];
                    var i2 = srctris[i * 3 + 2];
                    iout.Add(indexstart + i0);
                    iout.Add(indexstart + (flip ? i2 : i1));
                    iout.Add(indexstart + (flip ? i1 : i2));
                }
            }
        }

        void GenBorder(IList<Vector3> vout, IList<int> iout, IList<int> srcborder, int index1, int index2, bool flip)
        {
            for (var i = 0; i < srcborder.Count / 2; i++)
            {
                var p0 = srcborder[i * 2 + 0];
                var p1 = srcborder[i * 2 + 1];
                var v0 = vout[index1 + p0];
                var v1 = vout[index1 + p1];
                var v2 = vout[index2 + p0];
                var v3 = vout[index2 + p1];
                int index = vout.Count;
                vout.Add(v0);
                vout.Add(v1);
                vout.Add(v2);
                vout.Add(v3);
                iout.Add(index + 0);
                iout.Add(index + (flip ? 2 : 1));
                iout.Add(index + (flip ? 1 : 2));
                iout.Add(index + (flip ? 1 : 2));
                iout.Add(index + (flip ? 2 : 1));
                iout.Add(index + 3);
            }

        }
        public void Bake()
        {
            if (trier_config == null)
                trier_config = new TriangleNet.Meshing.ConstraintOptions()
                {

                };
            if (trier == null)
                trier = new TriangleNet.Meshing.Algorithm.Dwyer();


            if (points == null || points.Count == 0)
                throw new System.Exception("Bake error,poly points zero or null.");

            var vertext = new TriangleNet.Geometry.Vertex[points.Count];
            var polygon = new TriangleNet.Geometry.Polygon(points.Count, true);
            for (var i = 0; i < points.Count; i++)
            {
                vertext[i] = (new TriangleNet.Geometry.Vertex(points[i].x, points[i].y, 1));
            }
            TriangleNet.Geometry.Contour c = new TriangleNet.Geometry.Contour(vertext, 1, false);
            polygon.Add(c);
            var mesh = polygon.Triangulate(trier_config);


            //var mesh = trier.Triangulate(vertext, trier_config);
            //Debug.LogWarning("tri count=" + mesh.Triangles.Count + ", vcount=" + mesh.Vertices.Count);

            var meshfilter = this.GetComponent<MeshFilter>(); ;
            List<Vector3> srcvertex = new List<Vector3>();
            foreach (var s in mesh.Vertices)
            {
                srcvertex.Add(new UnityEngine.Vector3((float)s.x, (float)s.y, 0));
            }
            List<int> srctris = new List<int>();
            foreach (var t in mesh.Triangles)
            {
                srctris.Add(t.GetVertexID(0));
                srctris.Add(t.GetVertexID(1));
                srctris.Add(t.GetVertexID(2));
            }
            List<int> srcborder = new List<int>();
            foreach (var b in mesh.Edges)
            {
                srcborder.Add(b.P0);
                srcborder.Add(b.P1);
            }

            List<Vector3> vertexs = new List<Vector3>();
            List<int> indexs = new List<int>();

            //add bottombegin
            int bottombegin = 0;
            int topbegin = 0;
            int bottom_step_begin = 0;
            int top_step_begin = 0;
            float bottom_z = this.mirror ? -height : 0;


            //gen bottom
            if (!mirror)
            {
                GenPoly(vertexs, indexs, srcvertex, srctris, bottom_z, true, true);
            }
            else
            {
                if (!useStep)
                {
                    GenPoly(vertexs, indexs, srcvertex, srctris, bottom_z, true, true);
                }
                else
                {
                    GenPoly(vertexs, indexs, srcvertex, srctris, bottom_z, false, true);

                    bottom_step_begin = vertexs.Count;
                    GenPolyStep(vertexs, indexs, srcvertex, srctris, bottom_z - this.heightStep, this.heightStep, true);
                }

            }
            topbegin = vertexs.Count;
            //gen top
            if (useStep)
            {

                GenPoly(vertexs, indexs, srcvertex, srctris, this.height, false, false);
                top_step_begin = vertexs.Count;
                GenPolyStep(vertexs, indexs, srcvertex, srctris, height + this.heightStep, this.heightStep, false);
            }
            else
            {
                GenPoly(vertexs, indexs, srcvertex, srctris, this.height, true, false);
            }


            //border
            GenBorder(vertexs, indexs, srcborder, bottombegin, topbegin, false);
            if (useStep)
            {
                GenBorder(vertexs, indexs, srcborder, topbegin, top_step_begin, false);
                if (mirror)
                {
                    GenBorder(vertexs, indexs, srcborder, bottombegin, bottom_step_begin, true);
                }
            }


            var unitymesh = new UnityEngine.Mesh();

            unitymesh.vertices = vertexs.ToArray();
            unitymesh.triangles = indexs.ToArray();
            unitymesh.RecalculateNormals();
            unitymesh.RecalculateTangents();
            unitymesh.SetSubMesh(0, new UnityEngine.Rendering.SubMeshDescriptor()
            {
                indexCount = indexs.Count,
                topology = MeshTopology.Triangles,
            });

            meshfilter.mesh = unitymesh;
        }
    }
}